/*
-------------------------------------------------

	Vector.h

	Dmitriy Kuzmenko (Dgut)

-------------------------------------------------
*/

#ifndef __AUTUMN_VECTOR_H__
#define __AUTUMN_VECTOR_H__

#define _USE_MATH_DEFINES
#include <math.h>

namespace Autumn
{

template< typename T, int N >
class Vector
{
public:
	T								components[ N ];
	
	static const Vector< T, N >		Zero;
	
	inline							Vector( void );
	inline							Vector( const Vector< T, N > & vector );
	inline							Vector( T x, T y, T z = 0 );
	
	inline const T					Length( void ) const;
	inline const Vector< T, N >		Normalize( void ) const;
	inline const T					Angle( const Vector< T, N > & vector ) const;
	
	inline const bool				operator ==( const Vector< T, N > & vector ) const;
	inline const bool				operator !=( const Vector< T, N > & vector ) const;
	
	inline Vector< T, N > &			operator =( const Vector< T, N > & vector );
	inline Vector< T, N > &			operator +=( const Vector< T, N > & vector );
	inline Vector< T, N > &			operator -=( const Vector< T, N > & vector );
	inline Vector< T, N > &			operator *=( T value );
	inline Vector< T, N > &			operator /=( T value );
	
	inline const Vector< T, N >		operator -( void ) const;
	
	inline const Vector< T, N >		operator +( const Vector< T, N > & vector ) const;
	inline const Vector< T, N >		operator -( const Vector< T, N > & vector ) const;
	inline const Vector< T, N >		operator *( T value ) const;
	inline const T					operator *( const Vector< T, N > & vector ) const;
	inline const Vector< T, N >		operator /( T value ) const;

	template< typename T, int N >
	friend inline const Vector< T, N >	operator *( T value, const Vector< T, N > & vector );
	template< typename T, int N >
	friend inline const Vector< T, N >	operator /( T value, const Vector< T, N > & vector );

	inline T &						operator []( int index );
	inline const T &				operator []( int index ) const;
};

template< typename T, int N >
const Vector< T, N > Vector< T, N >::Zero = Vector< T, N >();

/*typedef Vector< int > veci;
typedef Vector< float > vecf;*/

template< typename T, int N >
inline Vector< T, N >::Vector( void )
{
	for( int i = 0; i < N; i++ )
		components[ i ] = 0;
}

template< typename T, int N >
inline Vector< T, N >::Vector( const Vector< T, N > & vector )
{
	for( int i = 0; i < N; i++ )
		components[ i ] = vector.components[ i ];
}

template< typename T, int N >
inline Vector< T, N >::Vector( T x, T y, T z )
{
	for( int i = 0; i < N; i++ )
		components[ i ] = ( &x )[ i ];
}

template< typename T, int N >
inline const T Vector< T, N >::Length( void ) const
{
	return sqrt( static_cast< float >( *this * *this ) );
}

template< typename T, int N >
inline const Vector< T, N > Vector< T, N >::Normalize( void ) const
{
	const T L = Length();
	
	if( !L || L == 1 )
		return *this;
	
	return *this / L;
}

template< typename T, int N >
inline const T Vector< T, N >::Angle( const Vector< T, N > & vector ) const
{
	const T L0 = Length();
	const T L1 = vector.Length();
	
	if( !L0 || !L1 )
		return 0;

	const float COS = ( *this * vector ) / ( L0 * L1 );
	
	if( COS >= 1 )
		return 0;
	if( COS <= -1 )
		return M_PI;
	
	return acos( COS );
}

template< typename T, int N >
inline const bool Vector< T, N >::operator ==( const Vector< T, N > & vector ) const
{
	for( int i = 0; i < N; i++ )
		if( components[ i ] != vector.components[ i ] )
			return false;

	return true;
}

template< typename T, int N >
inline const bool Vector< T, N >::operator !=( const Vector< T, N > & vector ) const
{	
	return !( *this == vector );
}

template< typename T, int N >
inline Vector< T, N > & Vector< T, N >::operator =( const Vector< T, N > & vector )
{
	for( int i = 0; i < N; i++ )
		components[ i ] = vector.components[ i ];
	
	return *this;
}

template< typename T, int N >
inline Vector< T, N > & Vector< T, N >::operator +=( const Vector< T, N > & vector )
{
	for( int i = 0; i < N; i++ )
		components[ i ] += vector.components[ i ];
	
	return *this;
}

template< typename T, int N >
inline Vector< T, N > & Vector< T, N >::operator -=( const Vector< T, N > & vector )
{
	/*for( int i = 0; i < N; i++ )
		components[ i ] -= vector.components[ i ];
	
	return *this;*/
	return *this += -vector;
}

template< typename T, int N >
inline Vector< T, N > & Vector< T, N >::operator *=( T value )
{
	for( int i = 0; i < N; i++ )
		components[ i ] *= value;
	
	return *this;
}

template< typename T, int N >
inline Vector< T, N > & Vector< T, N >::operator /=( T value )
{
	// return *this *= 1 / value;	don't work if integer

	for( int i = 0; i < N; i++ )
		components[ i ] /= value;
	
	return *this;
}

template< typename T, int N >
inline const Vector< T, N > Vector< T, N >::operator -( void ) const
{	
	return Vector( *this ) *= -1;
}

template< typename T, int N >
inline const Vector< T, N > Vector< T, N >::operator +( const Vector< T, N > & vector ) const
{	
	return Vector( *this ) += vector;
}

template< typename T, int N >
inline const Vector< T, N > Vector< T, N >::operator -( const Vector< T, N > & vector ) const
{
	return Vector( *this ) -= vector;
}

template< typename T, int N >
inline const Vector< T, N > Vector< T, N >::operator *( T value ) const
{
	return Vector( *this ) *= value;
}

template< typename T, int N >
inline const T Vector< T, N >::operator *( const Vector< T, N > & vector ) const
{
	T result = 0;

	for( int i = 0; i < N; i++ )
		result += components[ i ] * vector.components[ i ];

	return result;
}

template< typename T, int N >
inline const Vector< T, N > Vector< T, N >::operator /( T value ) const
{
	return Vector( *this ) /= value;
}

template< typename T, int N >
inline const Vector< T, N > operator *( T value, const Vector< T, N > & vector )
{
	//return Vector< T, N >( value * vector.x, value * vector.y );
	return *this * value;
}

template< typename T, int N >
inline const Vector< T, N > operator /( T value, const Vector< T, N > & vector )
{
	//return Vector< T, N >( value / vector.x, value / vector.y );
	Vector< T, N > result;

	for( int i = 0; i < N; i++ )
		result.components[ i ] = value / vector.components[ i ];

	return result;
}

template< typename T, int N >
inline T & Vector< T, N >::operator []( int index )
{
	return ( ( T * )this )[ index ];
}

template< typename T, int N >
inline const T & Vector< T, N >::operator []( int index ) const
{
	return ( ( T * )this )[ index ];
}

}	// namespace Autumn

typedef Autumn::Vector< float, 2 > vec2f;
typedef Autumn::Vector< float, 3 > vec3f;

#endif	// !!! __AUTUMN_VECTOR_H__ !!!